package supremeragcalc.character;

public class Char implements JobConstants {

	public static int BASE_HIT = 175;
	public static int BASE_FLEE = 150;
	public static int BASE_FLEE2 = 1;
	public static int BASE_CRIT = 1;
	public static int BASE_ASPD = 156;

	private int classID = NOVICE;
	private int baselevel = 1;
	private int joblevel = 1;

	private int HPRecovery = 0;
	private int HPAddtion = 0;
	private int ItemHPMultiplier = 1;

	private int SPRecoveryRate = 0;
	private int SPAddtion = 0;
	private int ItemSPMultiplier = 1;

	private int weight = 0;

	private int str1;
	private int agi1;
	private int int1;
	private int vit1;
	private int dex1;
	private int luk1;
	private int statPoints = 0;

	private int maxBaseLevel = 0;
	private int maxAtributes = 0;

	boolean transClass = false;
	boolean thirdClass = false;
	boolean autoIncrement = true;

	private int str2 = 0;
	private int agi2 = 0;
	private int vit2 = 0;
	private int int2 = 0;
	private int dex2 = 0;
	private int luk2 = 0;


	public Char() {
		this(false);
	}

	public Char(boolean transClass) {
		changeClass(NOVICE);

		baselevel = 1;
		str1 = 1;	str2 = 0;
		agi1 = 1;	agi2 = 0;
		int1 = 1;	int2 = 0;
		vit1 = 1;	vit2 = 0;
		dex1 = 1;	dex2 = 0;
		luk1 = 1;	luk2 = 0;

		this.transClass = transClass;
		statPoints = getStatPointConsumation(baselevel);

		maxBaseLevel = isThirdClass() ? 150 : 99;
		maxAtributes = isThirdClass() ? 120 : 99;
	}

	public boolean isTransClass() {
		return transClass;
	}

	public boolean isThirdClass() {
		return thirdClass;
	}

	public int getBaselevel() {
		return baselevel;
	}

	public String getClassName() {
		switch(classID) {
		case NOVICE: return "Novice";
		case SWORDSMAN: return "Swordsman";
		case MAGE: return "Magician";
		case ARCHER: return "Archer";
		case MERCHANT: return "Merchant";
		case ACOLYTE: return "Acolyte";
		case THIEF: return "Thief";

		default: return "Unknown class";
		}
	}

	public void changeClass(int classID) {
		this.classID = classID;
	}

	public int getHP() {
		// sigma is a sum of the arithmetic progression from 2 to the base lvl
		// Here used a [(firstElem(=2) + lastElem) * numElem-1]/2 (P.A. sum)
		double SigmaBLV = ( ( 2 + getBaselevel() )*( getBaselevel() - 1 ) )/2;

		double BASE_HP = Math.floor( ( ( 35 + getBaselevel()*getHPMultiplier() +
			SigmaBLV*getHPFactor() )*( 100 + getVit() )/100 ) );

		int HP = (int) Math.floor( ( BASE_HP + getHPAddtion() )*getItemHPMultiplier() );

		return HP;
	}

	public int getHPAddtion() {
		return HPAddtion;
	}

	public void setHPAddtion(int addtion) {
		HPAddtion = addtion;
	}

	public double getHPFactor() {
		return TableContents.charConstants[classID][2];
	}

	public double getHPMultiplier() {
		return TableContents.charConstants[classID][1];
	}

	public int getHPRecovery() {
		return HPRecovery;
	}

	public void setHPRecovery(int recovery) {
		HPRecovery = recovery;
	}

	public int getItemHPMultiplier() {
		return ItemHPMultiplier;
	}

	public void setItemHPMultiplier(int itemHPMultiplier) {
		ItemHPMultiplier = itemHPMultiplier;
	}

	public int getItemSPMultiplier() {
		return ItemSPMultiplier;
	}

	public void setItemSPMultiplier(int itemSPMultiplier) {
		ItemSPMultiplier = itemSPMultiplier;
	}

	public int getJoblevel() {
		return joblevel;
	}

	public void setJoblevel(int joblevel) {
		this.joblevel = joblevel;
	}

	public int getSP() {
		double SPFactor = getSPFactor();
		double BASE_SP = Math.floor( ( 10 + getBaselevel()*SPFactor )*( 100 + getInt() )/100 );
		BASE_SP = Math.floor( ( BASE_SP + SPAddtion )*ItemSPMultiplier );
		return (int)BASE_SP;
	}

	public int getSPAddtion() {
		return SPAddtion;
	}

	public void setSPAddtion(int addtion) {
		SPAddtion = addtion;
	}

	public double getSPFactor() {
		return TableContents.charConstants[classID][3];
	}

	public int getSPRecoveryRate() {
		return SPRecoveryRate;
	}


	public double getMaxWeight() {
		return 2000 + getWeightModifier() + getBaseStr() * 30;
	}

	public void incWeight(int weight) {
		this.weight += weight;
	}

	public double getWeightModifier() {
		return TableContents.charConstants[classID][0];
	}


	/**********************************************************
	 * Stats points
	 **********************************************************/
	public int getRemainStatPoints() {
		return statPoints;
	}

	public boolean existRemainStatPoint(int points) {
		return getRemainStatPoints() >= points;
	}

	private int getStatPointConsumation(int bLv) {
		if(bLv == 1) {
			return !isTransClass() ? 48 : 100;

		} else
			return (int)(Math.floor((bLv-1) / 5) + 3);
	}

	public void incRemainStatPoint(int points) {
		statPoints += points;
	}
	
	public void decRemainStatPoint(int points) {
		if (existRemainStatPoint(points)) {
			statPoints -= points;
		}
	}

		public void addStatPointByLevel(int bLv) {
		statPoints += getStatPointConsumation(bLv);
	}

	private int calcStatPoints(int stat) {
		if(stat >= 100 && stat <= 104)
			return 16;

		if(stat > 104 && stat <= 109)
			return 20;

		if(stat > 109 && stat <= 114)
			return 24;

		if(stat > 114)
			return 28;

		return (int) Math.floor((stat - 1)/10) + 2;
	}

	public int getStrStatPoint() {
		return calcStatPoints(str1);
	}

	public int getAgiStatPoint() {
		return calcStatPoints(agi1);
	}

	public int getVitStatPoint() {
		return calcStatPoints(vit1);
	}

	public int getIntStatPoint() {
		return calcStatPoints(int1);
	}

	public int getDexStatPoint() {
		return calcStatPoints(dex1);
	}

	public int getLukStatPoint() {
		return calcStatPoints(luk1);
	}

	/**********************************************************/


	public int getBaseStr() {
		return str1;
	}

	public int getStr2() {
		return str2;
	}

	public int getBaseAgi() {
		return agi1;
	}

	public int getAgi2() {
		return agi2;
	}

	public int getBaseDex() {
		return dex1;
	}

	public int getDex2() {
		return dex2;
	}

	public int getBaseInt() {
		return int1;
	}

	public int getInt2() {
		return int2;
	}

	public int getBaseLuk() {
		return luk1;
	}

	public int getLuk2() {
		return luk2;
	}

	public int getBaseVit() {
		return vit1;
	}

	public int getVit2() {
		return vit2;
	}

	/*******************************************************
	 * Increase Attributes
	 *******************************************************/
	private void incBaseStr() {
		if(!isThirdClass())
			if(str1 >= 99) return;

		else
			if(str1 >= 120) return;

		if(existRemainStatPoint(getStrStatPoint())) {
			decRemainStatPoint(getStrStatPoint());
			str1 += 1;
		}
	}
	private void incBaseAgi() {
		if(!isThirdClass())
			if(agi1 >= 99) return;

		else
			if(agi1 >= 120) return;

		if(existRemainStatPoint(getAgiStatPoint())) {
			decRemainStatPoint(getAgiStatPoint());
			agi1 += 1;
		}
	}
	private void incBaseInt() {
		if(!isThirdClass())
			if(int1 >= 99) return;

		else
			if(int1 >= 120) return;

		if(existRemainStatPoint(getIntStatPoint())) {
			decRemainStatPoint(getIntStatPoint());
			int1 += 1;
		}
	}
	private void incBaseVit() {
		if(!isThirdClass())
			if(vit1 >= 99) return;

		else
			if(vit1 >= 120) return;

		if(existRemainStatPoint(getVitStatPoint())) {
			decRemainStatPoint(getVitStatPoint());
			vit1 += 1;
		}
	}
	private void incBaseDex() {
		if(!isThirdClass())
			if(dex1 >= 99) return;

		else
			if(dex1 >= 120) return;

		if(existRemainStatPoint(getDexStatPoint())) {
			decRemainStatPoint(getDexStatPoint());
			dex1 += 1;
		}
	}
	private void incBaseLuk() {
		if(!isThirdClass())
			if(luk1 >= 99) return;

		else
			if(luk1 >= 120) return;

		if(existRemainStatPoint(getLukStatPoint())) {
			decRemainStatPoint(getLukStatPoint());
			luk1 += 1;
		}
	}

	/******************************************************/

	/**
	 * str1 + str2
	 * @return retorna a soma dos atributos de STR
	 */
	public int getStr() {
		return getBaseStr() + getStr2();
	}

	/**
	 * agi + agi2
	 * @return retorna a soma dos atributos de AGI
	 */
	public int getAgi() {
		return getBaseAgi() + getAgi2();
	}

	/**
	 * vit1 + vit2
	 * @return retorna a soma dos atributos de VIT
	 */
	public int getVit() {
		return getBaseVit() + getVit2();
	}

	/**
	 * int1 + int2
	 * @return retorna a soma dos atributos de INT
	 */
	public int getInt() {
		return getBaseInt() + getInt2();
	}

	/**
	 * dex1 + dex2
	 * @return retorna a soma dos atributos de DEX
	 */
	public int getDex() {
		return getBaseDex() + getDex2();
	}

	/**
	 * luk + luk2
	 * @return retorna a soma dos atributos de LUK
	 */
	public int getLuk() {
		return getBaseLuk() + getLuk2();
	}

	/******************************************************/

	/**
	 * Calcula o ataque físico do char
	 * @return o ATK de atributo do Char
	 */
	public int getAtk1() {
		double atk1 = getStr() * 1.2 + getDex()/5 + getLuk()/3 + getBaselevel()/4;
		return (int) Math.floor(atk1);
	}

	/**
	 * Calcula o ataque físico do char
	 * @return o ATK de equipamentos do Char
	 */
	public int getAtk2() {
		return 0;
	}

	/**
	 * Calcula o ataque mágico do char
	 * @return o MATK de atributo do Char
	 */
	public int getMatk1() {
		double matk1 = getInt()*1.5 + getDex()/5 + getLuk()/3 + getBaselevel()/4;
		return (int) Math.floor(matk1);
	}

	/**
	 * Calcula o ataque mágico do char
	 * @return o MATK de equipamentos do Char
	 */
	public int getMatk2() {
		return 0;
	}

	/**
	 * Calcula a defesa fisica do Char
	 * @return a MDEF de atributos do char
	 */
	public int getDef1() {
		double def1 = getStr()/5 + getVit()/2 + getAgi()/5 + getBaselevel()/6;
		return (int) Math.floor(def1);
	}

	/**
	 * PARA IMPLEMENTAR
	 * Calcula a defesa física do Char
	 * @return a DEF de equipamento do char
	 */
	public int getDef2() {
		return 0;
	}

	/**
	 * Calcula a defesa mágica do Char
	 * @return a MDEF de atributos do char
	 */
	public int getMdef1() {
		double mdef1 = getInt()/2 + getVit()/5 + getDex()/4 + getBaselevel()/6;
		return (int) Math.floor(mdef1);
	}

	/**
	 * PARA IMPLEMENTAR
	 * Calcula a defesa mágica do Char
	 * @return a MDEF de equipamento do char
	 */
	public int getMdef2() {
		return 0;
	}

	/**
	 * Calcula a precisão(HIT) do Char
	 * @return a HIT do Char
	 */
	public int getHit() {
		double h = BASE_HIT + getDex() + (getLuk() / 3) + getBaselevel();
		return (int) Math.floor(h);
	}

	/**
	 * Calcula a esquiva(FLEE) do Char
	 * @return a FLEE do Char.
	 */
	public int getFlee1() {
		double f = BASE_FLEE + getAgi() + getLuk()/5 + getBaselevel();
		return (int) Math.floor(f);
	}

	/**
	 * Calcula a Lucky Flee do Char
	 * @return a Lucky Flee do Char
	 */
	public int getFlee2() {
		double flee2 = BASE_FLEE2 + (getLuk() / 10);
		return (int) Math.floor(flee2);
	}

	/**
	 * Calcula a taxa de ataques críticos(CRIT) do Char
	 * @return os CRIT do Char
	 */
	public double getCrit() {
		double crit = BASE_CRIT + (getLuk() / 3);
		return crit;
	}

	/**
	 * Calcula a velocidade de ataque(ASPD) do char
	 * @return a ASPD calculada do Char
	 */
	public double getAspd() {
		double a = BASE_ASPD + (getAgi() / 4) + (getDex() / 10);
		return a;
	}

	/******************************************************/

	/**
	 * Aumenta em 1 o nível do Char.
	 */
	public void plusBaseLevel() {
		if(getBaselevel() >= maxBaseLevel) return;

		baselevel += 1;
		addStatPointByLevel(getBaselevel());
	}

	/**
	 * Aumenta em 1 a força (STR) do Char
	 */
	public void plusStr() {
		if(existRemainStatPoint(getStrStatPoint())) {
			incBaseStr();

		} else {
			if(autoIncrement) {
				plusBaseLevel();
				plusStr();
			}
		}
	}

	/**
	 * Aumenta em 1 a agilidade (AGI) do Char
	 */
	public void plusAgi() {
		if(existRemainStatPoint(getAgiStatPoint())) {
			incBaseAgi();

		} else {
			if(autoIncrement) {
				plusBaseLevel();
				plusAgi();
				return;
			}
		}
	}

	/**
	 * Aumenta em 1 a vitidade (VIT) do Char
	 */
	public void plusVit() {
		if(existRemainStatPoint(getVitStatPoint())) {
			incBaseVit();

		} else {
			if(autoIncrement) {
				plusBaseLevel();
				plusVit();
			}
		}
	}

	/**
	 * Aumenta em 1 a inteligência (INT) do Char
	 */
	public void plusInt() {
		if(existRemainStatPoint(getIntStatPoint())) {
			incBaseInt();

		} else {
			if(autoIncrement) {
				plusBaseLevel();
				plusInt();
			}
		}
	}

	/**
	 * Aumenta em 1 a destreza (DEX) do Char
	 */
	public void plusDex() {
		if(existRemainStatPoint(getDexStatPoint())) {
			incBaseDex();

		} else {
			if(autoIncrement) {
				plusBaseLevel();
				plusDex();
			}
		}
	}

	/**
	 * Aumenta em 1 a sorte (LUK) do Char
	 */
	public void plusLuk() {
		if(existRemainStatPoint(getLukStatPoint())) {
			incBaseLuk();

		} else {
			if(autoIncrement) {
				plusBaseLevel();
				plusLuk();
			}
		}
	}
}
